package io.github.kongweiguang.core.retry;


import io.github.kongweiguang.core.lang.Assert;

import java.time.Duration;
import java.util.function.Supplier;

/**
 * 重试工具类
 * 自定义功能请使用{@link RetryableTask}类
 *
 * @author kongweiguang
 * @see RetryableTask
 */
public class Retrys {

    /**
     * 根据异常信息进行重试
     * 没有返回值，重试执行方法
     *
     * @param run         执行方法
     * @param maxAttempts 最大的重试次数
     * @param delay       重试间隔
     * @param recover     达到最大重试次数后执行的备用方法
     * @param exs         指定的异常类型需要重试
     */
    public static void ofException(Runnable run, long maxAttempts, Duration delay, Runnable recover, Class<? extends Throwable>... exs) {
        Assert.isTrue(exs.length > 0, "exs cannot be empty");
        RetryableTask.retryForExceptions(run, exs)
                .maxAttempts(maxAttempts)
                .delay(delay)
                .execute()
                .get()
                .orElseGet(() -> {
                    recover.run();
                    return null;
                });
    }

    /**
     * 根据异常信息进行重试
     * 有返回值，重试执行方法
     *
     * @param sup         执行方法
     * @param maxAttempts 最大的重试次数
     * @param delay       重试间隔
     * @param recover     达到最大重试次数后执行的备用方法
     * @param exs         指定的异常类型需要重试
     * @param <T>         结果类型
     * @return 执行结果
     */
    public static <T> T ofException(Supplier<T> sup, long maxAttempts, Duration delay, Supplier<T> recover, Class<? extends Throwable>... exs) {
        Assert.isTrue(exs.length > 0, "exs cannot be empty");

        return RetryableTask.retryForExceptions(sup, exs)
                .maxAttempts(maxAttempts)
                .delay(delay)
                .execute()
                .get()
                .orElseGet(recover);
    }

    /**
     * 根据自定义结果进行重试
     * 没有返回值，重试执行方法
     *
     * @param run         执行方法
     * @param maxAttempts 最大的重试次数
     * @param delay       重试间隔
     * @param recover     达到最大重试次数后执行的备用方法
     * @param predicate   自定义重试条件
     */
    public static void ofPredicate(Runnable run, long maxAttempts, Duration delay, Supplier<Void> recover, RetryPre<Void> predicate) {
        RetryableTask.retryForPredicate(run, predicate)
                .delay(delay)
                .maxAttempts(maxAttempts)
                .execute()
                .get()
                .orElseGet(recover);
    }


    /**
     * 根据异常信息进行重试
     * 有返回值，重试执行方法
     *
     * @param sup         执行方法
     * @param maxAttempts 最大的重试次数
     * @param delay       重试间隔
     * @param recover     达到最大重试次数后执行的备用方法
     * @param predicate   自定义重试条件
     * @param <T>         结果类型
     * @return 执行结果
     */
    public static <T> T ofPredicate(Supplier<T> sup, long maxAttempts, Duration delay, Supplier<T> recover, RetryPre<T> predicate) {
        return RetryableTask.retryForPredicate(sup, predicate)
                .delay(delay)
                .maxAttempts(maxAttempts)
                .execute()
                .get()
                .orElseGet(recover);
    }

}
